#include <exception>
#include <utility>
#include "Expression.h"
#include "Parser\ExpressionParser.h"
#include "InternalStruct.h"

using namespace std;

namespace CExpression
{
	Expression::Expression():expData(new InternalStruct())
	{
	}

	Expression::Expression(const ExpressionString &exp):expData(new InternalStruct())
	{
		this->Compile(exp);
	}

	Expression::~Expression()
	{
		delete this->expData;
		this->expData=0;
	}

	void Expression::Compile(const ExpressionString &str)
	{
		ExpressionParser parser;
		parser.Parse(str);
		if(!parser.ErrorOccured())
		{
			this->expData->rootPtr=parser.RootNode();
			this->expData->variableNodes=parser.Variables();
			this->expData->errorOccured=false;
			this->expData->errMsg=TEXT("");
			this->expData->executeContext=WrapperPtr<ExecuteContext>(new ExecuteContext());
			this->expData->executed=false;
		}
		else
		{
			this->expData->errorOccured=true;
			this->expData->errMsg=parser.ErrorMsg();
		}
	}

	void Expression::Eval()
	{
		if(this->expData->rootPtr.get()!=0&&!this->expData->executed)
		{
			this->expData->rootPtr->Eval(*(this->expData->executeContext));
			this->expData->executed=true;
		}
	}

	ExecuteRunTimeType Expression::ResultType()
	{
		this->Eval();
		return this->expData->executeContext->TempResultType();
	}

	double Expression::DoubleResult()
	{
		this->Eval();
		return this->expData->executeContext->GlobalTempNum();
	}

	bool Expression::BoolResult()
	{
		this->Eval();
		return this->expData->executeContext->GlobalTempBoolean();
	}

	std::vector<const ExpressionString*>* Expression::VariableSymbols()
	{
		vector<const ExpressionString*> *result=0;
		try
		{
			result=new vector<const ExpressionString*>();
			for(map< ExpressionString,shared_ptr<VariableNode> >::iterator i=this->expData->variableNodes->begin();i!=this->expData->variableNodes->end();++i)
			{
				result->push_back(&(i->first));
			}
			return result;
		}
		catch(const exception &ex)
		{
			delete result;
			throw;
		}
	}

	void Expression::SetVariableValue(const ExpressionString &name,bool val)
	{
		if(this->expData->rootPtr.get()!=0)
		{
			map< ExpressionString,shared_ptr<VariableNode> >::iterator iter=this->expData->variableNodes->find(name);
			if(iter!=this->expData->variableNodes->end())
			{
				this->RemoveVariableValue(iter);
				iter->second->SetCurrentType(ExecuteRunTimeType::Boolean);
				this->expData->executeContext->BoolVariables().insert(make_pair(name,val));
				this->expData->executed=false;
			}
		}
	}

	void Expression::SetVariableValue(const ExpressionString &name,double val)
	{
		if(this->expData->rootPtr.get()!=0)
		{
			map< ExpressionString,shared_ptr<VariableNode> >::iterator iter=this->expData->variableNodes->find(name);
			if(iter!=this->expData->variableNodes->end())
			{
				this->RemoveVariableValue(iter);
				iter->second->SetCurrentType(ExecuteRunTimeType::Double);
				this->expData->executeContext->DoubleVariables().insert(make_pair(name,val));
				this->expData->executed=false;
			}
		}
	}

	bool Expression::ErrorOccured()
	{
		return this->expData->errorOccured;
	}
	ExpressionString Expression::ErrorMsg()
	{
		return this->expData->errMsg;
	}

	void Expression::RemoveVariableValue(map< ExpressionString,shared_ptr<VariableNode> >::iterator nodeIter)
	{
		if(nodeIter->second->CurrentType()==ExecuteRunTimeType::Boolean)
		{
			map<ExpressionString,bool>::iterator iter=this->expData->executeContext->BoolVariables().find(nodeIter->second->VariableName());
			if(iter!=this->expData->executeContext->BoolVariables().end())
			{
				this->expData->executeContext->BoolVariables().erase(nodeIter->second->VariableName());
			}
		}
		else if(nodeIter->second->CurrentType()==ExecuteRunTimeType::Double)
		{
			map<ExpressionString,double>::iterator iter=this->expData->executeContext->DoubleVariables().find(nodeIter->second->VariableName());
			if(iter!=this->expData->executeContext->DoubleVariables().end())
			{
				this->expData->executeContext->DoubleVariables().erase(nodeIter->second->VariableName());
			}
		}
	}
}